let s:self = { 'logger': api#import('logger'), 'timeout': 30 }

"------------------------------------------------------------
" 异步运行命令
"------------------------------------------------------------
function! async#run(cmd, callback) abort
    let task = {
                \ 'callback': empty(a:callback) ? function('s:callback') : a:callback,
                \ 'collect': function('s:collect'),
                \ 'on_exit': function('s:on_exit'),
                \ 'output': [],
                \ }
    let options = {
                \ "out_io": "pipe",
                \ "out_cb": task.collect,
                \ "exit_cb": task.on_exit,
                \ }
    let task.job = job_start(a:cmd, options)
    return task
endfunction

function! s:collect(channel, text) dict
    let self.output = add(self.output, a:text)
endfunction

function! s:callback(text) dict
    let msg = api#import('message')
    let output = join(a:text, '\n')
    call msg.debug(output)
    return output
endfunction

function! s:on_exit(channel, text) dict
    let self.result = call(self.callback, [self.output])
endfunction



" function! s:self.call(func, args, dict, options) dict
function! s:self.call(func, ...) dict
    let self.function = a:func

    if a:0 > 0
        let args = a:1
    else
        let args = []
    endif
    let self.arguments = args

    if a:0 > 1
        let dict = a:2
    else
        let dict = {}
    endif
    let self.dict = dict

    if a:0 > 2
        let options = a:3
    else
        let options = {}
    endif
    let timeout = get(options, 'timeout', s:self.timeout)
    let repeat = get(options, 'repeat', 1)
    let self.verbose = get(options, 'verbose', 1)
    let self.on_return = get(options, 'callback', self.default_on_return)
    let self.timer = timer_start(timeout, self.callback, {'repeat': repeat})
    return self
endfunction

function! s:self.callback(timer) dict
    if self.verbose
        call self.logger.debug('calling function ' . string(self.function))
    endif
    let self.retval = call(self.function, self.arguments, self.dict)
    return call(self.on_return, [self.retval, a:timer])
endfunction

function! s:self.default_on_return(retval, timer) dict
    if self.verbose
        if type(a:retval) == type([])
            let result = '[' . join(a:retval, ', ') . ']'
        else
            let result = ' ' . a:retval . ' '
        endif
        let func = string(self.function) . '(' . join(self.arguments, ', ') . ')'
        let text = 'async call ' . func . ' return value ' . result
        call s:self.logger.debug(text)
    endif
endfunction

function! s:self.map(expression, func, ...) dict
    if a:0 > 0
        let options = a:1
    else
        let options = {}
    endif
    if type(a:expression) == type([])
        call self.logger.debug('calling async.map ([' . join(a:expression, ', ') . '], ' . string(a:func) . ')')
        let self.list = a:expression
        call extend(options, {'repeat': len(self.list) + 1, 'verbose': 0}, "keep")
        let self.timer = self.call(self.item_callback, [a:func], self, options)
    else
        return self.map(items(a:expression), a:func, options)
    endif
endfunction

function! s:self.item_callback(func) dict
    if empty(self.list)
        return self.on_finish()
    endif
    let item = remove(self.list, -1)
    if self.verbose
        call self.logger.debug('calling function ' . string(a:func) . ' on item ' . item)
    endif
    let retval = call(a:func, [item])
    return call(self.on_item_return, [retval, self.timer])
endfunction

function! s:self.on_finish() dict
    call self.logger.debug('calling async.map finshied.')
endfunction

function! s:self.on_item_return(retval, timer) dict
    if self.verbose
        return self.on_return(a:retval, a:timer)
    endif
endfunction

function! api#async#get() abort
    return deepcopy(s:self)
endfunction
